package managedbean.Production;

import entity.ConsolidatedMthSalesFc;

import entity.Employee;
import entity.MaterialRequired;
import entity.Product;
import entity.ProductionSchedule;
import entity.ProductionScheduleDay;
import entity.RawMaterial;
import entity.SalesForecast;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedProperty;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import managedbean.UserSessionManagerBean;
import org.primefaces.component.chart.series.ChartSeries;
import org.primefaces.model.chart.CartesianChartModel;
import session.stateless.EmployeeSessionBeanLocal;
import session.stateless.LogSessionBeanLocal;
import session.stateless.MaterialRequirementPlanSessionBeanLocal;
import session.stateless.MessageSessionBeanLocal;
import session.stateless.ProductSessionBeanLocal;
import session.stateless.ProductionScheduleSessionBeanLocal;
import session.stateless.SalesForecastSessionBeanLocal;
import session.stateless.WarehouseSessionBeanLocal;

@ManagedBean(name = "productionScheduleManagerBean")
@RequestScoped
public class ProductionScheduleManagerBean {

    @ManagedProperty(value = "#{userSessionManagerBean}")
    private UserSessionManagerBean userSessionManagerBean;
    @EJB
    private SalesForecastSessionBeanLocal salesForecastSessionBeanLocal;
    @EJB
    private LogSessionBeanLocal logSessionBeanLocal;
    @EJB
    private ProductSessionBeanLocal productSessionBeanLocal;
    @EJB
    private ProductionScheduleSessionBeanLocal productionScheduleSessionBeanLocal;
    @EJB
    private EmployeeSessionBeanLocal employeeSessionBeanLocal;
    @EJB
    private MessageSessionBeanLocal messageSessionBeanLocal;
    @EJB
    private WarehouseSessionBeanLocal warehouseSessionBeanLocal;
    @EJB
    private MaterialRequirementPlanSessionBeanLocal materialRequirementPlanSessionBeanLocal;
    private String loadAddProductionSchedule_ph;
    private String loadAddProductionSchedule_ps;
    private String loadAddProductionSchedule_psd;
    private String loadUpdateProductionSchedule_up;
    private String loadUpdateProductionSchedule_ps;
    private DateFormat df = new SimpleDateFormat("dd-MM-yyyy");
    private ConsolidatedMthSalesFc consolidatedMthSalesFc;
    private List<ConsolidatedMthSalesFc> consolidatedMthSalesFcs;
    private String status;
    private ProductionScheduleDay productionScheduleDay = new ProductionScheduleDay();
    private ProductionScheduleDay selectedProductionScheduleDay = new ProductionScheduleDay();
    private ArrayList<ProductionSchedule> productionSchedules = new ArrayList<ProductionSchedule>();
    private ProductionSchedule productionSchedule;
    private ArrayList<Integer> days = new ArrayList<Integer>();
    private ArrayList<String> phs = new ArrayList<String>();
    private ArrayList<Product> products = new ArrayList<Product>();
    private Integer line;
    private String code;
    private Date ph;
    private Date minDate;
    private Date maxDate;
    private String selectedPh;
    private Double aveUtilizationRate = 0.0;
    private CartesianChartModel model = new CartesianChartModel();
    private Boolean metForecast = true;
    private ArrayList<SalesForecast> salesForecasts = new ArrayList<SalesForecast>();
    private ArrayList<ProductionScheduleDay> outstandingProductionScheduleDays = new ArrayList<ProductionScheduleDay>();
    private String request;

    public String getLoadAddProductionSchedule_ph() {
        try {

            consolidatedMthSalesFcs = (ArrayList<ConsolidatedMthSalesFc>) userSessionManagerBean.getSelectedItem();
            Calendar cal = Calendar.getInstance();
            cal.set(consolidatedMthSalesFcs.get(0).getSalesYear(), 0, 1);
            minDate = cal.getTime();
            cal.set(consolidatedMthSalesFcs.get(0).getSalesYear(), 11, 31);
            maxDate = cal.getTime();

            String url = "http://"
                    + userSessionManagerBean.getServerName() + ":"
                    + userSessionManagerBean.getServerPort()
                    + "/MerlionERP-war/resources/files/HOLIDAYS.txt";

            if (userSessionManagerBean.getParameters() == null) {
                phs = productionScheduleSessionBeanLocal.getPhs(url);
            } else {
                phs = (ArrayList<String>) userSessionManagerBean.getParameters();
            }

            phs = sortDays(phs);

        } catch (Exception exception) {
            try {

                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/searchProductionSchedule.xhtml");
            } catch (Exception ex) {
            }
        }

        return null;
    }

    public String getLoadAddProductionSchedule_ps() {

        userSessionManagerBean.setRequest("addProductionSchedule_ps.xhtml");

        consolidatedMthSalesFcs = (ArrayList<ConsolidatedMthSalesFc>) userSessionManagerBean.getSelectedItem();
        setProductionSchedules((ArrayList<ProductionSchedule>) userSessionManagerBean.getProductionSchedules());
        Collections.sort(productionSchedules);

        int months = 0;
        for (ProductionSchedule ps : productionSchedules) {
            aveUtilizationRate += ps.getUtilizationRate();
            months++;
        }
        aveUtilizationRate = round(aveUtilizationRate / months, 2);
        drawProductionGraph();
        try {
        } catch (Exception exception) {
            try {

                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/addProductionSchedule_ph.xhtml");
            } catch (Exception ex) {
            }
        }

        return null;
    }

    public String getLoadAddProductionSchedule_psd() {

        request = (String) userSessionManagerBean.getRequest();
        productionSchedule = (ProductionSchedule) userSessionManagerBean.getSelectedProductionSchedule();
        ArrayList<ProductionScheduleDay> psd = new ArrayList(productionSchedule.getProductionScheduleDays());
        Collections.sort(psd);
        productionSchedule.setProductionScheduleDays(psd);
        products = new ArrayList(productSessionBeanLocal.getProducts());

        if (request.compareTo("updateProductionSchedule_ps.xhtml") == 0) {
            days = (ArrayList<Integer>) userSessionManagerBean.getParameters();
        } else {
            phs = (ArrayList<String>) userSessionManagerBean.getParameters();
            days = getAllocatableDays(productionSchedule.getOperationYear(), productionSchedule.getOperationMonth() - 1);
        }
        try {
        } catch (Exception exception) {

            try {

                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/addProductionSchedule_ps.xhtml");
            } catch (Exception ex) {
            }
        }

        return null;
    }

    /**
     * @return the loadUpdateProductionSchedule_up
     */
    public String getLoadUpdateProductionSchedule_up() {
        salesForecasts = (ArrayList<SalesForecast>) userSessionManagerBean.getSalesForecasts();
        consolidatedMthSalesFcs = (ArrayList<ConsolidatedMthSalesFc>) userSessionManagerBean.getSelectedItem();
        consolidatedMthSalesFc = (ConsolidatedMthSalesFc) userSessionManagerBean.getParameters();
//        ArrayList<SalesForecast> sfcs = new ArrayList<SalesForecast>();
        ArrayList<Integer> forecastedDemand = getDemand();

        setProductionSchedules((ArrayList<ProductionSchedule>) userSessionManagerBean.getProductionSchedules());
        Collections.sort(productionSchedules);

        int months = 0;
        for (ProductionSchedule ps : productionSchedules) {
            aveUtilizationRate += ps.getUtilizationRate();
            months++;
        }
        aveUtilizationRate = round(aveUtilizationRate / months, 2);
        outstandingProductionScheduleDays = productionScheduleSessionBeanLocal.getProductionScheduleDays("Backorder");

        for (ProductionScheduleDay psd : outstandingProductionScheduleDays) {
            Double minNumberOfHalfHours = 0.0;
            while (minNumberOfHalfHours * psd.getProduct().getCapacityPerHour() < psd.getQuantity()) {
                minNumberOfHalfHours += 0.5;
            }
            psd.setUtilizedHours(minNumberOfHalfHours);
            psd.setQuantity((int) (psd.getProduct().getCapacityPerHour() * minNumberOfHalfHours));
        }


        int count = 0;
        for (ConsolidatedMthSalesFc cmsfc : consolidatedMthSalesFcs) {
            for (SalesForecast sfc : cmsfc.getSalesForecasts()) {
                if (sfc.getStatus().compareTo("New sales forecast") != 0) {
                    forecastedDemand.set(count, forecastedDemand.get(count) + sfc.getQuantity());
                    count++;
                }
            }
            count = 0;
        }

        count = 0;
        for (SalesForecast sfc : consolidatedMthSalesFc.getSalesForecasts()) {
            Integer safetyStockRequirement = (int) (forecastedDemand.get(count) / 12 * 0.12);
            Integer inventory = warehouseSessionBeanLocal.getInventory(Long.parseLong("1000"), sfc.getProduct().getCode());

            if (inventory < safetyStockRequirement) 
            {
                Double minNumberOfHalfHours = 0.0;
                while (minNumberOfHalfHours * sfc.getProduct().getCapacityPerHour() < safetyStockRequirement - inventory) {
                    minNumberOfHalfHours += 0.5;
                }
                ProductionScheduleDay psd = new ProductionScheduleDay(null, 2, sfc.getProduct(), "Safety stock",
                        (int) (minNumberOfHalfHours * sfc.getProduct().getCapacityPerHour()), 0, minNumberOfHalfHours);
                outstandingProductionScheduleDays.add(psd);

            }
            count++;

        }

        try {
        } catch (Exception exception) {
            try {

                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/searchConsolidatedMthSalesFc.xhtml?request=updateProductionSchedule");
            } catch (Exception ex) {
            }
        }

        return null;
    }

    public String getLoadUpdateProductionSchedule_ps() {

        userSessionManagerBean.setRequest("updateProductionSchedule_ps.xhtml");

        consolidatedMthSalesFc = (ConsolidatedMthSalesFc) userSessionManagerBean.getSelectedItem();
        setProductionSchedules((ArrayList<ProductionSchedule>) userSessionManagerBean.getProductionSchedules());
        Collections.sort(productionSchedules);

        int lines = 0;
        for (ProductionSchedule ps : productionSchedules) {
            ps = computeUtilization(ps);
            aveUtilizationRate += ps.getUtilizationRate();
            lines++;
        }
        aveUtilizationRate = round(aveUtilizationRate / lines, 2);
        drawMonthProductionGraph();
        try {
        } catch (Exception exception) {
            try {

                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/updateProductionSchedule_up.xhtml");
            } catch (Exception ex) {
            }
        }


        return null;
    }

    //<editor-fold defaultstate="collapsed" desc="Customer attributes. Click on the + sign on the left to edit the code.">
    /**
     * @return the consolidatedMthSalesFc
     */
    public ConsolidatedMthSalesFc getConsolidatedMthSalesFc() {
        return consolidatedMthSalesFc;
    }

    /**
     * @param consolidatedMthSalesFc the consolidatedMthSalesFc to set
     */
    public void setConsolidatedMthSalesFc(ConsolidatedMthSalesFc consolidatedMthSalesFc) {
        this.consolidatedMthSalesFc = consolidatedMthSalesFc;
    }

    /**
     * @return the consolidatedMthSalesFcs
     */
    public List<ConsolidatedMthSalesFc> getConsolidatedMthSalesFcs() {
        return consolidatedMthSalesFcs;
    }

    /**
     * @param consolidatedMthSalesFcs the consolidatedMthSalesFcs to set
     */
    public void setConsolidatedMthSalesFcs(List<ConsolidatedMthSalesFc> consolidatedMthSalesFcs) {
        this.consolidatedMthSalesFcs = consolidatedMthSalesFcs;
    }

    /**
     * @return the userSessionManagerBean
     */
    public UserSessionManagerBean getUserSessionManagerBean() {
        return userSessionManagerBean;
    }

    /**
     * @param userSessionManagerBean the userSessionManagerBean to set
     */
    public void setUserSessionManagerBean(UserSessionManagerBean userSessionManagerBean) {
        this.userSessionManagerBean = userSessionManagerBean;
    }

    /**
     * @return the status
     */
    public String getStatus() {
        return status;
    }

    /**
     * @param status the status to set
     */
    public void setStatus(String status) {
        this.status = status;
    }

    /**
     * @return the productionScheduleDay
     */
    public ProductionScheduleDay getProductionScheduleDay() {
        return productionScheduleDay;
    }

    /**
     * @param productionScheduleDay the productionScheduleDay to set
     */
    public void setProductionScheduleDay(ProductionScheduleDay productionScheduleDay) {
        this.productionScheduleDay = productionScheduleDay;
    }

    /**
     * @return the line
     */
    public Integer getLine() {
        return line;
    }

    /**
     * @param line the line to set
     */
    public void setLine(Integer line) {
        this.line = line;
    }

    /**
     * @return the days
     */
    public ArrayList<Integer> getDays() {
        return days;
    }

    /**
     * @param days the days to set
     */
    public void setDays(ArrayList<Integer> days) {
        this.days = days;
    }

    /**
     * @return the code
     */
    public String getCode() {
        return code;
    }

    /**
     * @param code the code to set
     */
    public void setCode(String code) {
        this.code = code;
    }

    /**
     * @return the phs
     */
    public ArrayList<String> getPhs() {
        return phs;
    }

    /**
     * @param phs the phs to set
     */
    public void setPhs(ArrayList<String> phs) {
        this.phs = phs;
    }

    /**
     * @return the ph
     */
    public Date getPh() {
        return ph;
    }

    /**
     * @param ph the ph to set
     */
    public void setPh(Date ph) {
        this.ph = ph;
    }

    /**
     * @return the selectedPh
     */
    public String getSelectedPh() {
        return selectedPh;
    }

    /**
     * @param selectedPh the selectedPh to set
     */
    public void setSelectedPh(String selectedPh) {
        this.selectedPh = selectedPh;
    }

    /**
     * @return the minDate
     */
    public Date getMinDate() {
        return minDate;
    }

    /**
     * @param minDate the minDate to set
     */
    public void setMinDate(Date minDate) {
        this.minDate = minDate;
    }

    /**
     * @return the maxDate
     */
    public Date getMaxDate() {
        return maxDate;
    }

    /**
     * @param maxDate the maxDate to set
     */
    public void setMaxDate(Date maxDate) {
        this.maxDate = maxDate;
    }

    /**
     * @param loadAddProductionSchedule_ph the loadAddProductionSchedule_ph to set
     */
    public void setLoadAddProductionSchedule_ph(String loadAddProductionSchedule_ph) {
        this.loadAddProductionSchedule_ph = loadAddProductionSchedule_ph;
    }

    /**
     * @param loadAddProductionSchedule_ps the loadAddProductionSchedule_ps to set
     */
    public void setLoadAddProductionSchedule_ps(String loadAddProductionSchedule_ps) {
        this.loadAddProductionSchedule_ps = loadAddProductionSchedule_ps;
    }

    /**
     * @param loadAddProductionSchedule_psd the loadAddProductionSchedule_psd to set
     */
    public void setLoadAddProductionSchedule_psd(String loadAddProductionSchedule_psd) {
        this.loadAddProductionSchedule_psd = loadAddProductionSchedule_psd;
    }

    /**
     * @return the productionSchedules
     */
    public ArrayList<ProductionSchedule> getProductionSchedules() {
        return productionSchedules;
    }

    /**
     * @param productionSchedules the productionSchedules to set
     */
    public void setProductionSchedules(ArrayList<ProductionSchedule> productionSchedules) {
        this.productionSchedules = productionSchedules;
    }

    /**
     * @return the productionSchedule
     */
    public ProductionSchedule getProductionSchedule() {
        return productionSchedule;
    }

    /**
     * @param productionSchedule the productionSchedule to set
     */
    public void setProductionSchedule(ProductionSchedule productionSchedule) {
        this.productionSchedule = productionSchedule;
    }

    /**
     * @return the products
     */
    public ArrayList<Product> getProducts() {
        return products;
    }

    /**
     * @param products the products to set
     */
    public void setProducts(ArrayList<Product> products) {
        this.products = products;
    }

    /**
     * @return the selectedProductionScheduleDay
     */
    public ProductionScheduleDay getSelectedProductionScheduleDay() {
        return selectedProductionScheduleDay;
    }

    /**
     * @param selectedProductionScheduleDay the selectedProductionScheduleDay to set
     */
    public void setSelectedProductionScheduleDay(ProductionScheduleDay selectedProductionScheduleDay) {
        this.selectedProductionScheduleDay = selectedProductionScheduleDay;
    }

    /**
     * @return the aveUtilizationRate
     */
    public Double getAveUtilizationRate() {
        return aveUtilizationRate;
    }

    /**
     * @param aveUtilizationRate the aveUtilizationRate to set
     */
    public void setAveUtilizationRate(Double aveUtilizationRate) {
        this.aveUtilizationRate = aveUtilizationRate;
    }

    /**
     * @return the model
     */
    public CartesianChartModel getModel() {
        return model;
    }

    /**
     * @param model the model to set
     */
    public void setModel(CartesianChartModel model) {
        this.model = model;
    }

    /**
     * @param loadUpdateProductionSchedule_up the loadUpdateProductionSchedule_up to set
     */
    public void setLoadUpdateProductionSchedule_up(String loadUpdateProductionSchedule_up) {
        this.loadUpdateProductionSchedule_up = loadUpdateProductionSchedule_up;
    }

    /**
     * @param loadUpdateProductionSchedule_ps the loadUpdateProductionSchedule_ps to set
     */
    public void setLoadUpdateProductionSchedule_ps(String loadUpdateProductionSchedule_ps) {
        this.loadUpdateProductionSchedule_ps = loadUpdateProductionSchedule_ps;
    }

    /**
     * @return the salesForecasts
     */
    public ArrayList<SalesForecast> getSalesForecasts() {
        return salesForecasts;
    }

    /**
     * @param salesForecasts the salesForecasts to set
     */
    public void setSalesForecasts(ArrayList<SalesForecast> salesForecasts) {
        this.salesForecasts = salesForecasts;
    }

    /**
     * @return the outstandingProductionScheduleDays
     */
    public ArrayList<ProductionScheduleDay> getOutstandingProductionScheduleDays() {
        return outstandingProductionScheduleDays;
    }

    /**
     * @param outstandingProductionScheduleDays the outstandingProductionScheduleDays to set
     */
    public void setOutstandingProductionScheduleDays(ArrayList<ProductionScheduleDay> outstandingProductionScheduleDays) {
        this.outstandingProductionScheduleDays = outstandingProductionScheduleDays;
    }

    //</editor-fold>
    public void drawProductionGraphSingle() {
        ArrayList<Integer> forecastedDemand = new ArrayList<Integer>();
        ArrayList<Integer> producedProducts = new ArrayList<Integer>();
        int count = 0;
        products.clear();

        for (SalesForecast sfc : consolidatedMthSalesFc.getSalesForecasts()) {
            forecastedDemand.add(0); //initialize arraylist
            producedProducts.add(0);
            products.add(sfc.getProduct());
        }

        for (ConsolidatedMthSalesFc cmsfc : consolidatedMthSalesFcs) {
            for (SalesForecast sfc : cmsfc.getSalesForecasts()) {
                forecastedDemand.set(count, forecastedDemand.get(count) + sfc.getQuantity());
                count++;
            }
            count = 0;
        }


        for (int i = 0; i < forecastedDemand.size(); i++) {
            forecastedDemand.set(i, forecastedDemand.get(i) / 12);
        }



        for (ProductionSchedule ps : productionSchedules) {
            for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                for (int i = 0; i < products.size(); i++) {
                    if (psd.getProduct().getCode().compareTo(products.get(i).getCode()) == 0) {
                        producedProducts.set(i, producedProducts.get(i) + psd.getQuantity());
                    }
                }
            }
        }

        count = 0;
        for (Product p : products) {
            ChartSeries demand = new ChartSeries();
            ChartSeries produced = new ChartSeries();
            demand.setLabel("Forecasted");
            produced.setLabel("To produce");
            demand.set(p.getName(), forecastedDemand.get(count));
            produced.set(p.getName(), producedProducts.get(count));

            if (forecastedDemand.get(count) > producedProducts.get(count)) {
                metForecast = false;
            }
            count++;
            model.addSeries(demand);
            model.addSeries(produced);

        }


    }

    public void drawProductionGraph() {
        ArrayList<Integer> forecastedDemand = new ArrayList<Integer>();
        ArrayList<Integer> producedProducts = new ArrayList<Integer>();
        int count = 0;
        products.clear();

        for (SalesForecast sfc : consolidatedMthSalesFcs.get(0).getSalesForecasts()) {
            forecastedDemand.add(0); //initialize arraylist
            producedProducts.add(0);
            products.add(sfc.getProduct());
        }

        for (ConsolidatedMthSalesFc cmsfc : consolidatedMthSalesFcs) {
            for (SalesForecast sfc : cmsfc.getSalesForecasts()) {
                forecastedDemand.set(count, forecastedDemand.get(count) + sfc.getQuantity());
                count++;
            }
            count = 0;
        }

        for (ProductionSchedule ps : productionSchedules) {
            for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                for (int i = 0; i < products.size(); i++) {
                    if (psd.getProduct().getCode().compareTo(products.get(i).getCode()) == 0) {
                        producedProducts.set(i, producedProducts.get(i) + psd.getQuantity());
                    }
                }
            }
        }

        count = 0;
        for (Product p : products) {
            ChartSeries demand = new ChartSeries();
            ChartSeries produced = new ChartSeries();
            demand.setLabel("Forecasted");
            produced.setLabel("To produce");
            demand.set(p.getName(), forecastedDemand.get(count));
            produced.set(p.getName(), producedProducts.get(count));

            if (forecastedDemand.get(count) > producedProducts.get(count)) {
                metForecast = false;
            }
            count++;
            model.addSeries(demand);
            model.addSeries(produced);

        }


    }

    public void drawMonthProductionGraph() {
        ArrayList<Integer> forecastedDemand = new ArrayList<Integer>();
        HashMap hashProduce = new HashMap();
        products.clear();

        for (SalesForecast sfc : consolidatedMthSalesFc.getSalesForecasts()) {
            forecastedDemand.add(sfc.getQuantity());
            hashProduce.put(sfc.getProduct().getCode(), 0);
            products.add(sfc.getProduct());
        }

        for (ProductionSchedule ps : productionSchedules) {
            for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                if (psd.getType().compareTo("Normal") == 0) {
                    hashProduce.put(psd.getProduct().getCode(), (Integer) hashProduce.get(psd.getProduct().getCode()) + psd.getQuantity());

                }
            }
        }

        int count = 0;
        for (Product p : products) {
            ChartSeries demand = new ChartSeries();
            ChartSeries produced = new ChartSeries();
            demand.setLabel("Forecasted");
            produced.setLabel("To produce");
            demand.set(p.getName(), forecastedDemand.get(count));
            produced.set(p.getName(), (Integer) hashProduce.get(p.getCode()));

            if (forecastedDemand.get(count) > (Integer) hashProduce.get(p.getCode())) {
                metForecast = false;
            }
            count++;
            model.addSeries(demand);
            model.addSeries(produced);
        }


    }

    public ProductionSchedule computeUtilization(ProductionSchedule productionSchedule) {
        ProductionSchedule ps = productionSchedule;
        Double totalUtilizedHours = 0.0;
        for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
            if (psd.getType().compareTo("OT - Backorder") != 0 && psd.getType().compareTo("OT - Safety stock") != 0) {
                totalUtilizedHours += psd.getUtilizedHours();
            } else {
   
            }
        }
        ps.setUtilizationRate(round((totalUtilizedHours / ps.getTotalNormalOperatingHours() * 100), 2));

        return ps;
    }

    public void computeProductionPlan(ActionEvent event) {

        ArrayList<Integer> totalDaysPerMth = new ArrayList<Integer>();
        for (int i = 0; i < 12; i++) {
            ArrayList<Integer> allocatableDays = getAllocatableDays(consolidatedMthSalesFcs.get(i).getSalesYear(), i);
            totalDaysPerMth.add(allocatableDays.size());

        }




        ArrayList<Integer> quantity = new ArrayList<Integer>(); //total demand per year for each product
        products = new ArrayList<Product>();
        for (int i = 0; i < consolidatedMthSalesFcs.get(0).getSalesForecasts().size(); i++) {
            products.add(consolidatedMthSalesFcs.get(0).getSalesForecasts().get(i).getProduct());
            quantity.add(0);//initialize arraylist
        }
        for (ConsolidatedMthSalesFc cmsfc : consolidatedMthSalesFcs) {
            for (int x = 0; x < cmsfc.getSalesForecasts().size(); x++) {
                quantity.set(x, quantity.get(x) + cmsfc.getSalesForecasts().get(x).getQuantity());
            }
        }

        ArrayList<Integer> maxProductionPerDay = new ArrayList<Integer>();  //maximum production a day @ 95% utilization
        ArrayList<Integer> minNumberOfDays = new ArrayList<Integer>();      //minimum no. of days to fulfill allocated monthly forecast


        int count = 0;
        for (Integer i : quantity) {
            Integer temp = (int) (products.get(count).getCapacityPerHour() * 8 * 0.95);  //per day at 95% utilization
            maxProductionPerDay.add(temp);



            int current = 0;

            while ((current * temp) < (i / 12)) {
                current++;
            }

            minNumberOfDays.add(current);
            count++;



        }



        for (int i = 0; i < 12; i++) {

            Boolean feasible = checkFeasible(totalDaysPerMth.get(i), line, minNumberOfDays);
            ArrayList<Integer> adjustedNumberOfDays = adjustNumberOfDays(totalDaysPerMth.get(i), line, minNumberOfDays, quantity);
            ArrayList<Integer> allocatableDays = getAllocatableDays(consolidatedMthSalesFcs.get(i).getSalesYear(), i);
            ArrayList<Integer> demandPerMth = new ArrayList<Integer>();

            for (int x = 0; x < quantity.size(); x++) {
                if (!feasible) {
                    demandPerMth.add((int) (products.get(x).getCapacityPerHour() * 0.9 * 8 * adjustedNumberOfDays.get(x)));
                } else {
                    demandPerMth.add(quantity.get(x) / 12);
                }
            }

            ArrayList<ProductionSchedule> productionScheduleForMth = createProductionSchedule(demandPerMth, products, i + 1, allocatableDays, adjustedNumberOfDays);
            for (ProductionSchedule ps : productionScheduleForMth) {
                getProductionSchedules().add(ps);
            }

        }


        userSessionManagerBean.setProductionSchedules(getProductionSchedules());
        userSessionManagerBean.setParameters(phs);
        try {
            FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                    + userSessionManagerBean.getServerName() + ":"
                    + userSessionManagerBean.getServerPort()
                    + "/MerlionERP-war/Web_Production/addProductionSchedule_ps.xhtml");
        } catch (Exception ex) {
        }



    }

    public ArrayList<ProductionSchedule> createProductionSchedule(ArrayList<Integer> demandPerMth, ArrayList<Product> products, Integer month, ArrayList<Integer> allocatableDays, ArrayList<Integer> adjustedNumberOfDays) {

        ArrayList<Integer> productionRates = new ArrayList<Integer>();
        ArrayList<Double> utilizedHours = new ArrayList<Double>();
        ArrayList<ProductionSchedule> productionScheduleForMth = new ArrayList<ProductionSchedule>();
        ArrayList<Integer> daysCount = new ArrayList<Integer>(adjustedNumberOfDays);
        Integer totalAllocatableDays = 0;

        int count = 0;
        for (Integer q : demandPerMth) {
            totalAllocatableDays += adjustedNumberOfDays.get(count);
            Integer productionPerDay = q / adjustedNumberOfDays.get(count);
            Double minNumberOfHalfHours = 0.0;

            for (int i = 0; i < 8 * 2; i++) {
                if (minNumberOfHalfHours * products.get(count).getCapacityPerHour() < productionPerDay) {
                    minNumberOfHalfHours += 0.5;
                }
            }
            utilizedHours.add(minNumberOfHalfHours);
            productionRates.add(((int) (minNumberOfHalfHours * products.get(count).getCapacityPerHour())));
            count++;
        }

        //Create production lines 
        Employee employee = employeeSessionBeanLocal.getEmployee(userSessionManagerBean.getUserId());
        for (int y = 0; y < line; y++) {
            ProductionSchedule ps = new ProductionSchedule(employee, month, consolidatedMthSalesFcs.get(0).getSalesYear(), y + 1, totalAllocatableDays,
                    totalAllocatableDays / line * 8, 0.0, totalAllocatableDays / line * 4, 0.0, 100.0, new ArrayList<ProductionScheduleDay>());
            productionScheduleForMth.add(ps);
        }

        count = 0;


        for (Integer i : allocatableDays) {

            for (ProductionSchedule ps : productionScheduleForMth) {
                if (daysCount.get(count) == 0) {
                    count++;
                }

                ProductionScheduleDay psd = new ProductionScheduleDay(ps, i, products.get(count), "Normal", productionRates.get(count), 0, utilizedHours.get(count));
                ps.getProductionScheduleDays().add(psd);
                ps.setTotalNormalUtilizedOperatingHours(ps.getTotalNormalUtilizedOperatingHours() + psd.getUtilizedHours());
                daysCount.set(count, daysCount.get(count) - 1);
            }
        }

        for (ProductionSchedule ps : productionScheduleForMth) {
            ps.setUtilizationRate(round(ps.getTotalNormalUtilizedOperatingHours() / ps.getTotalNormalOperatingHours() * 100, 2));
        }
        return productionScheduleForMth;

    }

    //Get total number of working days for the month (exclude PH and Weekends)
    public ArrayList<Integer> getAllocatableDays(int year, int month) {

        Calendar cal = Calendar.getInstance();
        cal.set(year, month, 1);
        int numberDays = cal.getActualMaximum(cal.DAY_OF_MONTH);
        int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
        ArrayList<Integer> allocatableDays = new ArrayList<Integer>();
        for (int i = 1; i < numberDays + 1; i++) {
            boolean isPh = false;
            for (String phDay : phs) {
                if (phDay.compareTo(((Integer) i).toString()) == 0) {
                    isPh = true;
                }
            }
            if (!isPh) {
                if (dayOfWeek != 1 && dayOfWeek != 7) {
                    allocatableDays.add(i);
                }
            }
            dayOfWeek++;
            if (dayOfWeek == 8) {
                dayOfWeek = 1;
            }
        }
        return allocatableDays;
    }

    public Boolean checkFeasible(Integer totalDaysPerMth, Integer numOfLine, ArrayList<Integer> minNumberOfDays) {
        double count = 0;
        for (Integer i : minNumberOfDays) {
            count += i;
        }
        count = (totalDaysPerMth * numOfLine * 0.95) - count; //get reminder @ 95% utilization
        if (count >= 0.0) {
            return true;
        }
        return false;
    }

    public ArrayList<Integer> adjustNumberOfDays(Integer totalDaysPerMth, Integer numOfLine, ArrayList<Integer> minNumberOfDays, ArrayList<Integer> quantity) {

        ArrayList<Integer> demandPerMth = new ArrayList<Integer>();
        ArrayList<Integer> adjustedMinNumberOfDays = new ArrayList(minNumberOfDays);
        int count = 0;
        for (Integer i : quantity) {
            demandPerMth.add(i / 12);
        }
        for (Integer i : adjustedMinNumberOfDays) {
            count += i;
        }



        if ((totalDaysPerMth * numOfLine) >= count) {
            count = totalDaysPerMth * numOfLine - count;    //days to distribute

            for (int i = 0; i < count; i++) {
                int index = 0;
                int largestProductionRatePerDay = 0;
                for (int x = 0; x < adjustedMinNumberOfDays.size(); x++) {
                    int temp = demandPerMth.get(x) / adjustedMinNumberOfDays.get(x);
                    if (temp > largestProductionRatePerDay) {
                        largestProductionRatePerDay = temp;
                        index = x;
                    }
                }
                adjustedMinNumberOfDays.set(index, adjustedMinNumberOfDays.get(index) + 1);
            }

        } else {


            count = count - totalDaysPerMth * numOfLine;    //days to cut down
            for (int i = 0; i < count; i++) {
                int index = 0;
                int smallestProductionRatePerDay = 4000000;
                for (int x = 0; x < adjustedMinNumberOfDays.size(); x++) {
                    int temp = demandPerMth.get(x) / adjustedMinNumberOfDays.get(x);
                    if (temp < smallestProductionRatePerDay) {
                        smallestProductionRatePerDay = temp;
                        index = x;
                    }
                }
                adjustedMinNumberOfDays.set(index, adjustedMinNumberOfDays.get(index) - 1);
            }

        }
        return adjustedMinNumberOfDays;

    }

    public void editProductionPlan(ActionEvent event) {
        if (productionSchedule != null) {
            userSessionManagerBean.setSelectedProductionSchedule(productionSchedule);
            try {
                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/addProductionSchedule_psd.xhtml");
            } catch (Exception ex) {
            }

        }
    }

    public void removeProductionScheduleDay(ActionEvent event) {
        if (productionScheduleDay != null) {
            productionSchedule.setTotalNormalUtilizedOperatingHours(
                    productionSchedule.getTotalNormalUtilizedOperatingHours()
                    - selectedProductionScheduleDay.getUtilizedHours());
            productionSchedule.getProductionScheduleDays().remove(selectedProductionScheduleDay);
            productionSchedule = computeUtilization(productionSchedule);
            userSessionManagerBean.getProductionSchedules().remove(productionSchedule);
            userSessionManagerBean.getProductionSchedules().add(productionSchedule);
            userSessionManagerBean.setSelectedProductionSchedule(productionSchedule);
            try {
                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/addProductionSchedule_psd.xhtml");
            } catch (Exception ex) {
            }

        }
    }

    public void addProductionScheduleDay(ActionEvent event) {
        if (productionScheduleDay.getQuantity() == 0 || code.compareTo("0") == 0 || productionScheduleDay.getCurrentDay() == 0) {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "One or more fields filled in incorrectly.", ""));
        } else {

            productionScheduleDay.setProduct(productSessionBeanLocal.getProduct(code));
            productionScheduleDay.setType("Normal");

            Double minNumberOfHalfHours = 0.0;

            for (int i = 0; i < 8 * 2; i++) {
                if (minNumberOfHalfHours * productionScheduleDay.getProduct().getCapacityPerHour() < productionScheduleDay.getQuantity()) {
                    minNumberOfHalfHours += 0.5;
                }
            }
            productionScheduleDay.setUtilizedHours(minNumberOfHalfHours);
            productionScheduleDay.setQuantity((int) (productionScheduleDay.getProduct().getCapacityPerHour() * minNumberOfHalfHours));




            ArrayList<ProductionScheduleDay> psds = new ArrayList<ProductionScheduleDay>();
            Boolean found = false;
            Double totalUtilizedHours = 0.0;


            for (ProductionScheduleDay psd : productionSchedule.getProductionScheduleDays()) {
                if (psd.getCurrentDay().compareTo(productionScheduleDay.getCurrentDay()) == 0) {
                    psds.add(psd);
                    totalUtilizedHours += psd.getUtilizedHours();
                    if (psd.getProduct().getCode().compareTo(productionScheduleDay.getProduct().getCode()) == 0) {
                        found = true;
                        selectedProductionScheduleDay = psd;
                    }
                }
            }



            if (found) {
                totalUtilizedHours += productionScheduleDay.getUtilizedHours() - 0.5;
                for (ProductionScheduleDay psd : productionSchedule.getProductionScheduleDays()) {
                    if (psd.getCurrentDay() == selectedProductionScheduleDay.getCurrentDay()) {
                        totalUtilizedHours += 0.5;
                    }

                }
            } else {
                totalUtilizedHours += productionScheduleDay.getUtilizedHours();
                for (ProductionScheduleDay psd : productionSchedule.getProductionScheduleDays()) {
                    if (psd.getCurrentDay() == productionScheduleDay.getCurrentDay()) {
                        totalUtilizedHours += 0.5;
                    }

                }
            }

            if (totalUtilizedHours <= 8.0) {
                if (found) {
                    productionSchedule.getProductionScheduleDays().remove(selectedProductionScheduleDay);
                    selectedProductionScheduleDay.setQuantity(selectedProductionScheduleDay.getQuantity() + productionScheduleDay.getQuantity());
                    selectedProductionScheduleDay.setUtilizedHours(selectedProductionScheduleDay.getUtilizedHours() + productionScheduleDay.getUtilizedHours());
                    productionSchedule.getProductionScheduleDays().add(selectedProductionScheduleDay);
                } else {
                    productionSchedule.getProductionScheduleDays().add(productionScheduleDay);

                }

                productionSchedule = computeUtilization(productionSchedule);
                userSessionManagerBean.getProductionSchedules().remove(productionSchedule);
                userSessionManagerBean.getProductionSchedules().add(productionSchedule);
                userSessionManagerBean.setSelectedProductionSchedule(productionSchedule);

                try {
                    FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                            + userSessionManagerBean.getServerName() + ":"
                            + userSessionManagerBean.getServerPort()
                            + "/MerlionERP-war/Web_Production/addProductionSchedule_psd.xhtml");
                } catch (Exception ex) {
                }

            } else {
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "You can only allocate max. 8 hours of production schedule. There is a 0.5 hr per switch over.", ""));
            }
        }
    }

    public void doneProductionScheduleDay(ActionEvent event) {
        try {
            if (request.compareTo("updateProductionSchedule_ps.xhtml") == 0) {
                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/updateProductionSchedule_ps.xhtml");
            } else {
                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/addProductionSchedule_ps.xhtml");
            }
        } catch (Exception ex) {
        }

    }

    public void doneUpdateProductionSchedule(ActionEvent event) {
       try {
            HashMap hasRawMaterial = new HashMap();
            products.clear();
            productionScheduleSessionBeanLocal.removeProductionSchedule(consolidatedMthSalesFc.getSalesMonth(), consolidatedMthSalesFc.getSalesYear());
            productionScheduleSessionBeanLocal.removeProductionScheduleDays("Backorder");
            for (ProductionSchedule ps : productionSchedules) {
                productionScheduleSessionBeanLocal.addProductionSchedule(ps);
                for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                    products.add(psd.getProduct());

                    ArrayList<MaterialRequired> materialRequired = new ArrayList(psd.getProduct().getMaterialRequired());
                    for (MaterialRequired mr : materialRequired) {
                        Double totalWt = 0.0;
                        totalWt = psd.getQuantity() * psd.getProduct().getWeight();
                        totalWt /= 1000;
                        totalWt /= 200;
                        totalWt *= mr.getWeight();
                        RawMaterial rm = mr.getRawMaterial();


                        if (hasRawMaterial.get(rm.getId()) == null) {
                            hasRawMaterial.put(rm.getId(), totalWt);
                        } else {
                            hasRawMaterial.put(rm.getId(), round ((Double) (hasRawMaterial.get(rm.getId())) + totalWt,0));
                        }

                    }
                }
            }
            Calendar cal = Calendar.getInstance();
            Integer year = consolidatedMthSalesFc.getSalesYear();
            Integer month = consolidatedMthSalesFc.getSalesMonth();
            Integer day = 30;

            if (month - 1 == 0) {
                month = 12;
                year--;
            }

            for (RawMaterial rm : productSessionBeanLocal.getRawMaterials()) {
                Integer leadTime = rm.getLeadTime();
                cal.set(year, month - 1, day - 2 - (leadTime * 7));
                Date d = cal.getTime();
                materialRequirementPlanSessionBeanLocal.addMaterialRequirementPlan(d, (Double) hasRawMaterial.get(rm.getId()), rm);
            }

            HashSet hs = new HashSet(products);
            products.clear();
            products.addAll(hs);

            for (Product product : products) {
                salesForecastSessionBeanLocal.updateSalesForecastStatus(consolidatedMthSalesFc.getSalesYear(), consolidatedMthSalesFc.getSalesMonth(), product.getCode(), "Pending for production", false);
            }


 
            ArrayList<String> al = new ArrayList<String>();
            al.add("Production schedule successfully finalized");
            al.add("You have successfully created the final production schedule for Month/Year:" + consolidatedMthSalesFc.getSalesMonth() +"/"+consolidatedMthSalesFc.getSalesYear() + ".");
            al.add("Click on the link to return to ");
            al.add("continue");
            al.add("Web_Production/searchConsolidatedMthSalesFc.xhtml?request=updateProductionSchedule_up");
            userSessionManagerBean.setSelectedItem(al);
            logSessionBeanLocal.addLog("Create production schedule", "Final production schedule created  Month/Year: " + consolidatedMthSalesFc.getSalesMonth() +"/"+consolidatedMthSalesFc.getSalesYear(), "updateProductionSchedule_ps.xhtml", userSessionManagerBean.getUserId());

            FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                    + userSessionManagerBean.getServerName() + ":"
                    + userSessionManagerBean.getServerPort()
                    + "/MerlionERP-war/success.xhtml");
            
            

        } catch (Exception ex) {
            System.out.println(ex.toString());
        }


    }

    public void doneProductionSchedule(ActionEvent event) {
        try {

            for (ProductionSchedule ps : productionSchedules) {
                productionScheduleSessionBeanLocal.addProductionSchedule(ps);
            }

            ArrayList<Employee> employees = new ArrayList<Employee>();
            for (ConsolidatedMthSalesFc cmsfc : consolidatedMthSalesFcs) {
                salesForecastSessionBeanLocal.updateSalesForecastStatus(cmsfc.getSalesYear(), cmsfc.getSalesMonth(), "Initial production schedule", true);
                for (SalesForecast sfc : cmsfc.getSalesForecasts()) {
                    employees.add(sfc.getSalesRep());

                }
            }
            Set<Employee> em = new LinkedHashSet<Employee>(employees);
            employees.clear();
            employees = new ArrayList(em);

            for (Employee employee : employees) {

                if (metForecast) {
                    Employee owner = employeeSessionBeanLocal.getEmployee("System");
                    messageSessionBeanLocal.addMessage("System", employee.getUserId(), "Sales forecast acknowledged",
                            "Sales forecast for Year: " + consolidatedMthSalesFcs.get(0).getSalesYear() + "</b> has been acknowledged by " + userSessionManagerBean.getUserId() + ". Production able to meet sales forecast. No follow up action require."
                            + "<p><hr/><p>This is a system generated message, no reply is required.", owner);

                    owner = employee;
                    messageSessionBeanLocal.addMessage("System", employee.getUserId(), "Sales forecast acknowledged",
                            "Sales forecast for Year: " + consolidatedMthSalesFcs.get(0).getSalesYear() + "</b> has been acknowledged by " + userSessionManagerBean.getUserId() + ". Production able to meet sales forecast. No follow up action require."
                            + "<p><hr/><p>This is a system generated message, no reply is required.", owner);
                } else {
                    Employee owner = employeeSessionBeanLocal.getEmployee("System");
                    messageSessionBeanLocal.addMessage("System", employee.getUserId(), "Sales forecast rejected",
                            "Sales forecast for Year: " + consolidatedMthSalesFcs.get(0).getSalesYear() + "</b> has been acknowledged by " + userSessionManagerBean.getUserId() + ". Production unable to meet sales forecast. No follow up action require."
                            + "<p><hr/><p>This is a system generated message, no reply is required.", owner);

                    owner = employee;
                    messageSessionBeanLocal.addMessage("System", employee.getUserId(), "Sales forecast rejected",
                            "Sales forecast for Year: " + consolidatedMthSalesFcs.get(0).getSalesYear() + "</b> has been acknowledged by " + userSessionManagerBean.getUserId() + ". Production unable to meet sales forecast. No follow up action require."
                            + "<p><hr/><p>This is a system generated message, no reply is required.", owner);
                }

            }

            ArrayList<String> al = new ArrayList<String>();
            al.add("Production schedule successfully created");
            al.add("You have successfully created the production schedule for " + consolidatedMthSalesFcs.get(0).getSalesYear() + ".");
            al.add("Click on the link to return to ");
            al.add("continue");
            al.add("Web_Production/searchConsolidatedMthSalesFc.xhtml?request=addProductionSchedule");
            userSessionManagerBean.setSelectedItem(al);
            logSessionBeanLocal.addLog("Create production schedule", "Production schedule created Year: " + consolidatedMthSalesFcs.get(0).getSalesYear(), "addProductionSchedule_ps.xhtml", userSessionManagerBean.getUserId());



            FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                    + userSessionManagerBean.getServerName() + ":"
                    + userSessionManagerBean.getServerPort()
                    + "/MerlionERP-war/success.xhtml");

        } catch (Exception ex) {
            System.out.println(ex.toString());
        }


    }

    //Functions for PH
    public void addDate(ActionEvent event) {
        if (ph != null) {
            DateFormat df = new SimpleDateFormat("dd-MM-yyyy");
            String phStr = df.format(ph);
            phs.add(phStr);
            userSessionManagerBean.setParameters(phs);
            try {
                FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                        + userSessionManagerBean.getServerName() + ":"
                        + userSessionManagerBean.getServerPort()
                        + "/MerlionERP-war/Web_Production/addProductionSchedule_ph.xhtml");

            } catch (Exception ex) {
                System.out.println(ex.toString());
            }

        }


    }

    public void removeDate(ActionEvent event) {
        if (selectedPh != null) {
            phs.remove(selectedPh);
        }
        userSessionManagerBean.setParameters(phs);
        try {
            FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                    + userSessionManagerBean.getServerName() + ":"
                    + userSessionManagerBean.getServerPort()
                    + "/MerlionERP-war/Web_Production/addProductionSchedule_ph.xhtml");

        } catch (Exception ex) {
            System.out.println(ex.toString());
        }

    }

    public ArrayList<String> sortDays(ArrayList<String> dates) {
        ArrayList<String> sortedDays = new ArrayList<String>();
        while (dates.size() > sortedDays.size()) {
            String date = findSmallest(dates, sortedDays);
            sortedDays.add(date);
        }
        return sortedDays;
    }

    public String findSmallest(ArrayList<String> dates, ArrayList<String> sortedDays) {


        int dd = 99;
        int mm = 99;
        int yyyy = 9999;
        for (String date : dates) {
            if (!sortedDays.contains(date)) {
                String[] split = date.split("-");

                if (Integer.parseInt(split[2]) < yyyy) {
                    dd = Integer.parseInt(split[0]);
                    mm = Integer.parseInt(split[1]);
                    yyyy = Integer.parseInt(split[2]);

                } else {
                    if (Integer.parseInt(split[2]) == yyyy && Integer.parseInt(split[1]) < mm) {
                        dd = Integer.parseInt(split[0]);
                        mm = Integer.parseInt(split[1]);
                        yyyy = Integer.parseInt(split[2]);
                    } else {
                        if (Integer.parseInt(split[2]) == yyyy && Integer.parseInt(split[1]) == mm
                                && Integer.parseInt(split[0]) < dd) {
                            dd = Integer.parseInt(split[0]);
                            mm = Integer.parseInt(split[1]);
                            yyyy = Integer.parseInt(split[2]);
                        } else {
                        }
                    }
                }
            }
        }
        if (dd < 10 && mm < 10) {
            return "0" + dd + "-0" + mm + "-" + yyyy;
        } else if (dd < 10) {
            return "0" + dd + mm + "-" + yyyy;
        } else if (mm < 10) {
            return +dd + "-0" + mm + "-" + yyyy;
        } else {
            return dd + "-" + mm + "-" + yyyy;
        }
    }

    public double round(double valueToRound, int numberOfDecimalPlaces) {
        double multiplicationFactor = Math.pow(10, numberOfDecimalPlaces);
        double interestedInZeroDPs = valueToRound * multiplicationFactor;

        return Math.round(interestedInZeroDPs) / multiplicationFactor;
    }

    public void updateProductionSchedule(ActionEvent event) {

        products = new ArrayList(productSessionBeanLocal.getProducts());
        ArrayList<Integer> updatedDemand = getUpdatedDemand();
        ArrayList<Integer> currentProduce = getCurrentProduce();
        ArrayList<Integer> allocatableDays = new ArrayList<Integer>();

        HashMap hashProduce = new HashMap();

        for (int i = 0; i < updatedDemand.size(); i++) {
            if (updatedDemand.get(i) == 0) {
                hashProduce.put(products.get(i).getCode(), currentProduce.get(i));
            } else {
                hashProduce.put(products.get(i).getCode(), updatedDemand.get(i));
            }
        }

        //scale down demand
        ArrayList<ProductionScheduleDay> remove_psd = new ArrayList<ProductionScheduleDay>();
        for (ProductionSchedule ps : productionSchedules) {
            for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                allocatableDays.add(psd.getCurrentDay());
                if (psd.getQuantity() != psd.getReservedQuantity() && psd.getReservedQuantity() > 0) {
                    Double minNumberOfHalfHours = 0.0;
                    while (minNumberOfHalfHours * psd.getProduct().getCapacityPerHour() < psd.getReservedQuantity()) {
                        minNumberOfHalfHours += 0.5;
                    }
                    psd.setUtilizedHours(minNumberOfHalfHours);
                    psd.setQuantity((int) (psd.getProduct().getCapacityPerHour() * minNumberOfHalfHours));
                    hashProduce.put(psd.getProduct().getCode(), ((Integer) hashProduce.get(psd.getProduct().getCode())) - psd.getQuantity());
                } else {
                    remove_psd.add(psd);
                }

            }
        }

        HashSet h = new HashSet(allocatableDays);
        allocatableDays.clear();
        allocatableDays.addAll(h);
        Collections.sort(allocatableDays);




        for (int i = 0; i < remove_psd.size(); i++) {
            ProductionSchedule main_ps = remove_psd.get(i).getProductionSchedule();
            for (ProductionSchedule ps : productionSchedules) {
                if (ps.getId().compareTo(main_ps.getId()) == 0) {
                    ps.getProductionScheduleDays().remove(remove_psd.get(i));
                }
            }
        }

        userSessionManagerBean.setParameters(allocatableDays);

        ArrayList<ProductionScheduleDay> add_psd = new ArrayList<ProductionScheduleDay>();
        Boolean found = false;
        int count = 0;
        double leftover = 0;
        for (Integer i : allocatableDays) {

            for (ProductionSchedule ps : productionSchedules) {
                for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                    if (i == psd.getCurrentDay()) {
                        found = true;
                        if (getRemainingHours(ps.getLineNumber(), psd.getCurrentDay()) < 8.1 && outstandingProductionScheduleDays.get(count).getUtilizedHours() > 0) {
                            double commit = 8 - getRemainingHours(ps.getLineNumber(), psd.getCurrentDay());
                            if (commit > outstandingProductionScheduleDays.get(count).getUtilizedHours()) {
                                leftover = commit - outstandingProductionScheduleDays.get(count).getUtilizedHours();
                                commit = outstandingProductionScheduleDays.get(count).getUtilizedHours();
                            }
                            ProductionScheduleDay psday = new ProductionScheduleDay(ps, i, psd.getProduct(), "Normal - " + outstandingProductionScheduleDays.get(count).getType(),
                                    (int) (commit * psd.getProduct().getCapacityPerHour()), (int) (commit * psd.getProduct().getCapacityPerHour()), commit);
                            psday.setPurchaseItems(outstandingProductionScheduleDays.get(count).getPurchaseItems());
                            outstandingProductionScheduleDays.get(count).setUtilizedHours(outstandingProductionScheduleDays.get(count).getUtilizedHours() - commit);
                            add_psd.add(psday);

                            if (leftover > 0) {
                                psday = new ProductionScheduleDay(ps, i, psd.getProduct(), "Normal",
                                        (int) (leftover * psd.getProduct().getCapacityPerHour()), 0, leftover);
                                hashProduce.put(psd.getProduct().getCode(), ((Integer) hashProduce.get(psd.getProduct().getCode())) - psday.getQuantity());
                                add_psd.add(psday);
                                leftover = 0;
                            }
                        }
                    } else {
                        for (ProductionScheduleDay apsd : add_psd) {
                            if (apsd.getCurrentDay().compareTo(i) == 0) {
                                found = true;
                            }
                        }
                    }
                }
                if (!found) {
                    if (outstandingProductionScheduleDays.get(count).getUtilizedHours() > 0) {
                        double commit = 8;
                        if (commit > outstandingProductionScheduleDays.get(count).getUtilizedHours()) {
                            leftover = commit - outstandingProductionScheduleDays.get(count).getUtilizedHours();
                            commit = outstandingProductionScheduleDays.get(count).getUtilizedHours();
                        }
                        ProductionScheduleDay psday = new ProductionScheduleDay(ps, i, outstandingProductionScheduleDays.get(count).getProduct(), "Normal - " + outstandingProductionScheduleDays.get(count).getType(),
                                (int) (commit * outstandingProductionScheduleDays.get(count).getProduct().getCapacityPerHour()), (int) (commit * outstandingProductionScheduleDays.get(count).getProduct().getCapacityPerHour()), commit);
                        psday.setPurchaseItems(outstandingProductionScheduleDays.get(count).getPurchaseItems());
                        outstandingProductionScheduleDays.get(count).setUtilizedHours(outstandingProductionScheduleDays.get(count).getUtilizedHours() - commit);
                        add_psd.add(psday);

                        if (leftover > 0) {
                            psday = new ProductionScheduleDay(ps, i, outstandingProductionScheduleDays.get(count).getProduct(), "Normal",
                                    (int) (leftover * outstandingProductionScheduleDays.get(count).getProduct().getCapacityPerHour()), 0, leftover);
                            hashProduce.put(outstandingProductionScheduleDays.get(count).getProduct().getCode(), ((Integer) hashProduce.get(outstandingProductionScheduleDays.get(count).getProduct().getCode())) - psday.getQuantity());
                            add_psd.add(psday);
                            leftover = 0;
                        }

                    }
                }

                if (outstandingProductionScheduleDays.get(count).getType().compareTo("Backorder") == 0 && outstandingProductionScheduleDays.get(count).getUtilizedHours() > 0 && !found) {

                    double commit = 4;
                    if (commit > outstandingProductionScheduleDays.get(count).getUtilizedHours()) {
                        leftover = commit - outstandingProductionScheduleDays.get(count).getUtilizedHours();
                        commit = outstandingProductionScheduleDays.get(count).getUtilizedHours();
                    }
                    ProductionScheduleDay psday = new ProductionScheduleDay(ps, i, outstandingProductionScheduleDays.get(count).getProduct(), "OT - " + outstandingProductionScheduleDays.get(count).getType(),
                            (int) (commit * outstandingProductionScheduleDays.get(count).getProduct().getCapacityPerHour()), (int) (commit * outstandingProductionScheduleDays.get(count).getProduct().getCapacityPerHour()), commit);
                    psday.setPurchaseItems(outstandingProductionScheduleDays.get(count).getPurchaseItems());
                    outstandingProductionScheduleDays.get(count).setUtilizedHours(outstandingProductionScheduleDays.get(count).getUtilizedHours() - commit);
                    add_psd.add(psday);

                    if (leftover > 0) {
                        psday = new ProductionScheduleDay(ps, i, outstandingProductionScheduleDays.get(count).getProduct(), "Normal",
                                (int) (leftover * outstandingProductionScheduleDays.get(count).getProduct().getCapacityPerHour()), 0, leftover);
                        hashProduce.put(outstandingProductionScheduleDays.get(count).getProduct().getCode(), ((Integer) hashProduce.get(outstandingProductionScheduleDays.get(count).getProduct().getCode())) - psday.getQuantity());
                        add_psd.add(psday);
                        leftover = 0;
                    }
                }
                found = false;

                if (outstandingProductionScheduleDays.get(count).getUtilizedHours() == 0) {
                    if (count < outstandingProductionScheduleDays.size() - 1) {
                        count++;
                    }
                }
            }
            leftover = 0;
        }

        for (int i = 0; i < add_psd.size(); i++) {
            ProductionSchedule main_ps = add_psd.get(i).getProductionSchedule();
            for (ProductionSchedule ps : productionSchedules) {
                if (ps.getId().compareTo(main_ps.getId()) == 0) {
                    ps.getProductionScheduleDays().add(add_psd.get(i));
                }
            }
        }
        add_psd.clear();


        ArrayList<Integer> requiredDays = new ArrayList<Integer>();
        for (Product product : products) {
            Integer minNumberOfDays = 0;
            while (minNumberOfDays * 8 * product.getCapacityPerHour() < ((Integer) hashProduce.get(product.getCode()))) {
                minNumberOfDays++;
            }

            requiredDays.add(minNumberOfDays);
        }

        count = 0;

        for (Integer i : allocatableDays) {

            for (ProductionSchedule ps : productionSchedules) {
                found = false;
                for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                    if (psd.getCurrentDay() == i) {
                        found = true;
                    }
                }
                if (!found) {
                    count++;
                }
            }
        }



        ArrayList<Integer> adjustedMinNumberOfDays = adjustNumebrOfDaysSingleMonth(count, requiredDays, hashProduce, products);
        ArrayList<Integer> productionPerDay = getProductionPerDay(adjustedMinNumberOfDays, hashProduce, products);




        found = false;
        count = 0;
        for (Integer i : allocatableDays) {
            for (ProductionSchedule ps : productionSchedules) {
                if (adjustedMinNumberOfDays.get(count) == 0) {
                    count++;
                }
                for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                    if (psd.getCurrentDay().compareTo(i) == 0) {
                        found = true;
                    }

                }
                if (!found) {

                    Double minNumberOfHalfHours = 0.0;

                    for (int x = 0; x < 8 * 2; x++) {
                        if (minNumberOfHalfHours * products.get(count).getCapacityPerHour() < productionPerDay.get(count)) {
                            minNumberOfHalfHours += 0.5;
                        }
                    }

                    ProductionScheduleDay psday = new ProductionScheduleDay(ps, i, products.get(count), "Normal", (int) (minNumberOfHalfHours * products.get(count).getCapacityPerHour()), 0, minNumberOfHalfHours);
                    add_psd.add(psday);
                    adjustedMinNumberOfDays.set(count, adjustedMinNumberOfDays.get(count) - 1);
                }
                found = false;
            }

        }

        for (int i = 0; i < add_psd.size(); i++) {
            ProductionSchedule main_ps = add_psd.get(i).getProductionSchedule();
            for (ProductionSchedule ps : productionSchedules) {
                if (ps.getId().compareTo(main_ps.getId()) == 0) {
                    ps.getProductionScheduleDays().add(add_psd.get(i));
                }
            }
        }


        userSessionManagerBean.setProductionSchedules(productionSchedules);

        consolidatedMthSalesFc.getSalesForecasts().clear();
        for (Product product : products) {
            SalesForecast sfc = new SalesForecast(consolidatedMthSalesFc.getSalesMonth(), consolidatedMthSalesFc.getSalesYear(), null, product,
                    (Integer) hashProduce.get(product.getCode()), null, null, null);
            consolidatedMthSalesFc.getSalesForecasts().add(sfc);

        }

        userSessionManagerBean.setSelectedItem(consolidatedMthSalesFc);
        try {
            FacesContext.getCurrentInstance().getExternalContext().redirect("http://"
                    + userSessionManagerBean.getServerName() + ":"
                    + userSessionManagerBean.getServerPort()
                    + "/MerlionERP-war/Web_Production/updateProductionSchedule_ps.xhtml");
        } catch (Exception ex) {
        }


    }

    public ArrayList<Integer> getProductionPerDay(ArrayList<Integer> adjustedMinNumberOfDays, HashMap hashProduce, ArrayList<Product> products) {
        ArrayList<Integer> productionPerDay = new ArrayList<Integer>();
        for (int i = 0; i < adjustedMinNumberOfDays.size(); i++) {
            Integer quantity = ((Integer) hashProduce.get(products.get(i).getCode())) / adjustedMinNumberOfDays.get(i);
            if (quantity > (products.get(i).getCapacityPerHour() * 8 * 0.9)) {
                quantity = (int) (products.get(i).getCapacityPerHour() * 8 * 0.9);
            }
            productionPerDay.add(quantity);

        }
        return productionPerDay;
    }

    public ArrayList<Integer> adjustNumebrOfDaysSingleMonth(Integer reminder, ArrayList<Integer> requiredDays, HashMap hashProduce, ArrayList<Product> products) {

        ArrayList<Integer> demandPerMth = new ArrayList<Integer>();
        ArrayList<Integer> adjustedMinNumberOfDays = new ArrayList(requiredDays);
        Integer totalRequiredDays = 0;

        for (Integer i : requiredDays) {
            totalRequiredDays += i;
        }

        for (Product product : products) {
            demandPerMth.add((Integer) hashProduce.get(product.getCode()));
        }
        int count = 0;

        if (reminder > totalRequiredDays) {
            count = reminder - totalRequiredDays;    //days to distribute

            for (int i = 0; i < count; i++) {
                int index = 0;
                int largestProductionRatePerDay = 0;
                for (int x = 0; x < adjustedMinNumberOfDays.size(); x++) {
                    int temp = demandPerMth.get(x) / adjustedMinNumberOfDays.get(x);
                    if (temp > largestProductionRatePerDay) {
                        largestProductionRatePerDay = temp;
                        index = x;
                    }
                }
                adjustedMinNumberOfDays.set(index, adjustedMinNumberOfDays.get(index) + 1);
            }

        } else {

            count = totalRequiredDays - reminder; //days to cut down
            for (int i = 0; i < count; i++) {
                int index = 0;
                int smallestProductionRatePerDay = 4000000;
                for (int x = 0; x < adjustedMinNumberOfDays.size(); x++) {
                    int temp = demandPerMth.get(x) / adjustedMinNumberOfDays.get(x);
                    if (temp < smallestProductionRatePerDay) {
                        smallestProductionRatePerDay = temp;
                        index = x;
                    }
                }
                adjustedMinNumberOfDays.set(index, adjustedMinNumberOfDays.get(index) - 1);
            }

        }
        return adjustedMinNumberOfDays;

    }

    public Double getRemainingHours(Integer line, Integer day) {
        ArrayList<ProductionScheduleDay> productionScheduleDays = new ArrayList<ProductionScheduleDay>();
        ArrayList<Product> typeOfProduct = new ArrayList<Product>();
        for (ProductionSchedule ps : productionSchedules) {
            if (ps.getLineNumber().compareTo(line) == 0) {
                for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                    if (psd.getCurrentDay().compareTo(day) == 0) {
                        productionScheduleDays.add(psd);
                        typeOfProduct.add(psd.getProduct());
                    }
                }

            }
        }

        HashSet set = new HashSet(typeOfProduct);
        typeOfProduct.clear();
        typeOfProduct.addAll(set);

        Double hours = 0.0;
        for (ProductionScheduleDay psd : productionScheduleDays) {
            hours += psd.getUtilizedHours();
        }
        hours += (typeOfProduct.size() - 1) * 0.5;
        return hours;
    }

    public ArrayList<Integer> getUpdatedDemand() {
        ArrayList<Integer> updatedDemand = new ArrayList<Integer>();
        HashMap hs = new HashMap();
        for (Product product : products) {
            hs.put(product.getCode(), 0);
        }
        for (SalesForecast sfc : salesForecasts) {
            hs.put(sfc.getProduct().getCode(), sfc.getQuantity());
        }
        for (Product product : products) {
            updatedDemand.add((Integer) hs.get(product.getCode()));
        }
        return updatedDemand;
    }

    public ArrayList<Integer> getDemand() {
        ArrayList<Integer> forecastedDemand = new ArrayList<Integer>();
        int count = 0;

        for (SalesForecast sfc : consolidatedMthSalesFc.getSalesForecasts()) {
            forecastedDemand.add(0); //initialize ArrayList

        }
        for (ConsolidatedMthSalesFc cmsfc : consolidatedMthSalesFcs) {
            for (SalesForecast sfc : cmsfc.getSalesForecasts()) {
                if (sfc.getStatus().compareTo("New sales forecast") != 0) {
                    forecastedDemand.set(count, forecastedDemand.get(count) + sfc.getQuantity());
                    count++;
                }
            }
            count = 0;
        }
        return forecastedDemand;

    }

    public ArrayList<Integer> getCurrentProduce() {
        HashMap hashProduce = new HashMap();
        ArrayList<Integer> currentProduction = new ArrayList<Integer>();
        for (Product product : productSessionBeanLocal.getProducts()) {
            hashProduce.put(product.getCode(), 0);
        }
        for (ProductionSchedule ps : productionSchedules) {
            for (ProductionScheduleDay psd : ps.getProductionScheduleDays()) {
                int qty = ((Integer) (hashProduce.get(psd.getProduct().getCode())));
                qty += psd.getQuantity();
                hashProduce.put(psd.getProduct().getCode(), qty);

            }
        }

        for (Product product : productSessionBeanLocal.getProducts()) {
            currentProduction.add((Integer) (hashProduce.get(product.getCode())));
        }

        return currentProduction;
    }
}
